package com.heima.article.service.impl;

import java.util.Date;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 热点文章计算 前五天
 */
@Slf4j
@Service
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    ApArticleMapper articleMapper;

    /**
     * 热点文章计算
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void computeHotArticle() {
        //查询出前五天的热点文章数据
        //查询出五天前的时间 获取当前时间
        String localData = LocalDateTime.now().minusDays(5).format(DateTimeFormatter
                .ofPattern("yyyy-MM-dd HH:mm:ss")).toString();
        //ApArticleMapper执行方法查询得到一个文章集合
        List<ApArticle> apArticleList = articleMapper.selectArticleByDate(localData);
        //计算每篇文章的分数  转成vo集合
        List<HotArticleVo> hotArticleVoList = computeScoreAndTransalte(apArticleList);
        //按频道缓存热点文章
        sendArticleToRedis(hotArticleVoList);
    }

    @Override
    public void updateApArticle(AggBehaviorDTO mess) {
        //根据文章id查询出文章数据
        ApArticle apArticle = articleMapper.selectById(mess.getArticleId());


        if (apArticle==null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"未找到该文章");
        }
        //根据聚合结果,更新文章行为参数
        apArticle.setLikes((int)(apArticle.getLikes()==null?
                mess.getLike(): apArticle.getLikes() + mess.getLike()));

        apArticle.setCollection((int)(apArticle.getCollection()==null?
                mess.getCollect(): apArticle.getCollection() + mess.getCollect()));

        apArticle.setComment((int)(apArticle.getComment()==null?
                mess.getComment(): apArticle.getComment() + mess.getComment()));

        apArticle.setViews((int)(apArticle.getViews()==null?
                mess.getView(): apArticle.getViews() + mess.getView()));
        //重新计算得分
        Integer score = computeScore(apArticle);
        //判断文章是否是当日发布 如果是 热度* 3
        String dateTime = DateUtils.dateToString(new Date()); //当前时间
        String publishTime = apArticle.getPublishTime().toString();//文章发布时间
        if (dateTime.equals(publishTime)){
            score=score*3;
        }
        //更新所在频道热点缓存
        updateArticleCache(apArticle,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());
        //更新热点文章列表到redis中
        updateArticleCache(apArticle,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    }

    /**
     * 根据 最新文章热度得分  更新redis缓存
     * @param apArticle 文章对象
     * @param score 文章热度分数
     * @param cacheKey redis key
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey) {
        //设置一个旗帜 默认值为false
        boolean flag=false;
        //拿到未更新前的热点文章列表
        String hotArticvleList = redisTemplate.opsForValue().get(cacheKey);
        //判断是否为空
        if (StringUtils.isBlank(hotArticvleList)) {
           CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"热点缓存为空");
        }
        //不为空 将字符串转换为list集合
        List<HotArticleVo> articleVos = JSON.parseArray(hotArticvleList, HotArticleVo.class);
        //如果缓存中存在该文章,更新分值 遍历集合
        for (HotArticleVo articleVo : articleVos) {
            //判断文章id是否相等
            if (articleVo.getId().equals(apArticle.getId())){
                //更新分值
                articleVo.setScore(score);
                //将flag默认值修改为true
                flag=true;
                //到这步可以直接停止程序
                break;
            }
        }
        //如果缓存中没有该文章
        if (!flag){
            //创建vo对象
            HotArticleVo vo = new HotArticleVo();
            //拷贝属性
            BeanUtils.copyProperties(apArticle,vo);
            //设置最新分值
            vo.setScore(score);
            //将vo对象添加进vo集合
            articleVos.add(vo);
        }
        //将热点文章集合  按得分降序排序  截取前30条热点文章
        articleVos=articleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        //将更新后的热点文章存储到redis中
        redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(articleVos));
    }

    //注入adminfeign
    @Autowired
    AdminFeign adminFeign;

    /**
     * 缓存热点文章
     *
     * @param hotArticleVoList
     */
    private void sendArticleToRedis(List<HotArticleVo> hotArticleVoList) {
        //远程查询频道列表
        ResponseResult<List<AdChannel>> result = adminFeign.selectChannels();
        //判断状态码
        if (!result.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR, "远程调用AdminFeign接口失败");
        }
        //拿出频道数据
        List<AdChannel> channels = result.getData();
        //判断集合是否为空
        if (CollectionUtils.isEmpty(channels)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "未查询到频道数据");
        }

        //遍历频道集合 筛选当前频道下的文章
        for (AdChannel channel : channels) {
            //过滤
            List<HotArticleVo> hotArticleVos = hotArticleVoList.stream()
                    .filter(hotArticle -> hotArticle.getChannelId().equals(channel.getId()))
                    .collect(Collectors.toList());
            //进行排序 并缓存到redis
            sortAndCache(hotArticleVos, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channel.getId());
        }
        //给推荐频道缓存30条文章  所有文章排序后的前30条
        sortAndCache(hotArticleVoList,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    }

    //注入redisTemplate
    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 缓存热点文章
     *
     * @param hotArticleVos 热点文章列表
     * @param key
     */
    private void sortAndCache(List<HotArticleVo> hotArticleVos, String key) {
        //对热点文章进行排序
        hotArticleVos = hotArticleVos.stream()
                //根据分数进行降序排序
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                //截取30条
                .limit(30)
                //收集成vo集合
                .collect(Collectors.toList());
        //将热点文章存储进redis中  将集合转为JSON字符串存储进redis中
        redisTemplate.opsForValue().set(key, JSON.toJSONString(hotArticleVos));
    }

    /**
     * 将apArticleList转为hotArticleVoList,并实现计算分值
     *
     * @param apArticleList
     * @return
     */
    private List<HotArticleVo> computeScoreAndTransalte(List<ApArticle> apArticleList) {
        //这里采用stream流进行抽取集合
        return apArticleList.stream().map(article -> {
            HotArticleVo hotArticleVo = new HotArticleVo();
            //拷贝属性
            BeanUtils.copyProperties(article, hotArticleVo);
            //计算分值
            Integer score = computeScore(article);
            //设置分值
            hotArticleVo.setScore(score);
            //返回vo对象
            return hotArticleVo;
        }).collect(Collectors.toList());
    }

    /**
     * 计算每篇文章的分值
     *
     * @param apArticle 文章对象
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;
        // 阅读 1
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}